package be.rivendale.mathematics;

import static be.rivendale.mathematics.MathematicalAssert.assertPointEquals;
import be.rivendale.mathematics.intersection.Intersection;
import be.rivendale.mathematics.intersection.RayIntersectionMode;
import org.junit.Assert;
import org.junit.Test;

public class RectangleTest {
    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointAIsNull() throws Exception {
        new Rectangle(null, new Triple(4, 5, 6), new Triple(8, 7, 9));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointBIsNull() throws Exception {
        new Rectangle(new Triple(1, 2, 3), null, new Triple(8, 7, 9));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointCIsNull() throws Exception {
        new Rectangle(new Triple(1, 2, 3), new Triple(4, 5, 6), null);
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointsAreNotOnAPlane() throws Exception {
        new Rectangle(new Triple(1, 2, 3), new Triple(4, 5, 6), new Triple(7, 8, 9));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointsPointAAndPointBAreEqual() throws Exception {
        new Rectangle(new Triple(1, 2, 3), new Triple(1, 2, 3), new Triple(7, 8, 9));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointsPointBAndPointCAreEqual() throws Exception {
        new Rectangle(new Triple(1, 2, 3), new Triple(7, 8, 9), new Triple(7, 8, 9));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointsPointAAndPointCAreEqual() throws Exception {
        new Rectangle(new Triple(1, 2, 3), new Triple(7, 8, 9), new Triple(1, 2, 3));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointsDoNotDefinedAPerpendicularAngle() throws Exception {
        new Rectangle(new Triple(1, 2, 3), new Triple(6, 1, 0), new Triple(-2, -5, 1));
    }

    @Test
    public void constructorsSetsPointsAsPointAPointBAndPointC() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(0, 0, 0), new Triple(5, 0, 0), new Triple(0, 5, 0));
        assertPointEquals(new Triple(0, 0, 0), rectangle.getA());
        assertPointEquals(new Triple(5, 0, 0), rectangle.getB());
        assertPointEquals(new Triple(0, 5, 0), rectangle.getC());
    }

    @Test
    public void constructorCalculatesPointDBasedOnOtherPointsWhenNoneOfThePointsAreNegative() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(0, 0, 0), new Triple(5, 0, 0), new Triple(0, 5, 0));
        assertPointEquals(new Triple(5, 5, 0), rectangle.getD());
    }

    @Test
    public void constructorCalculatesPointDBasedOnOtherPointsWhenSomeOfThePointsAreNegative() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(-8, 4.5, -5),
                new Triple(-8, -4.5, -5),
                new Triple(8, 4.5, -5));
        assertPointEquals(new Triple(8, -4.5, -5), rectangle.getD());
    }

    @Test(expected = IllegalArgumentException.class)
    public void pointOnRectangleThrowsIllegalArgumentExceptionWhenTheFirstDimensionIsLessThenZero() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(0, 0, 0), new Triple(0, 1, 0), new Triple(1, 0, 0));
        rectangle.pointOnRectangle(-0.1, 0.5);
        Assert.fail();
    }

    @Test(expected = IllegalArgumentException.class)
    public void pointOnRectangleThrowsIllegalArgumentExceptionWhenTheFirstDimensionIsGreaterThenOne() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(0, 0, 0), new Triple(0, 1, 0), new Triple(1, 0, 0));
        rectangle.pointOnRectangle(1.1, 0.5);
    }

    @Test(expected = IllegalArgumentException.class)
    public void pointOnRectangleThrowsIllegalArgumentExceptionWhenTheSecondDimensionIsLessThenZero() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(0, 0, 0), new Triple(0, 1, 0), new Triple(1, 0, 0));
        rectangle.pointOnRectangle(0.5, -0.1);
    }

    @Test(expected = IllegalArgumentException.class)
    public void pointOnRectangleThrowsIllegalArgumentExceptionWhenTheSecondDimensionIsGreaterThenOne() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(0, 0, 0), new Triple(0, 1, 0), new Triple(1, 0, 0));
        rectangle.pointOnRectangle(0.5, 1.1);
    }

    @Test
    public void pointOnRectangleReturnsTheMiddleOfTheRectangleWhenBothDimensionsAreHalf() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(-1, -1, 0), new Triple(-1, 1, 0), new Triple(1, -1, 0));
        Point middlePoint = rectangle.pointOnRectangle(0.5, 0.5);
        Assert.assertEquals(new Triple(0, 0, 0), middlePoint);
    }

    @Test
    public void pointOnRectangleReturnsTheCorrectPointWithinTheRectangle() throws Exception {
        Rectangle rectangle = new Rectangle(new Triple(1, 1, 2), new Triple(1, -1, 2), new Triple(-1, 1, -2));
        Point point = rectangle.pointOnRectangle(0.75, 0.66);
        MathematicalAssert.assertPointEquals(new Triple(-0.32, -0.5, -0.64), point);
    }

	@Test
	public void intersectionYieldsValidIntersectionWhenTheRectangleIntersectsWithTheRay() throws Exception {
		Rectangle rectangle = new Rectangle(
				new Vertex(0, 0, 0),
				new Vertex(0, 1, 0),
				new Vertex(1, 0, 1)
		);
		Ray ray = new Ray(new Vertex(0.5, 0.5, 1), new Vertex(0.5, 0.5, -1));
		Intersection intersection = rectangle.intersection(ray, RayIntersectionMode.betweenOriginAndPassThroughPoint);
		Assert.assertTrue(intersection.doesIntersect());
	}

	@Test
	public void intersectionYieldsInvalidIntersectionWhenTheRectangleIntersectsWithTheRay() throws Exception {
		Rectangle rectangle = new Rectangle(
				new Vertex(0, 0, 0),
				new Vertex(0, 1, 0),
				new Vertex(1, 0, 1)
		);
		Ray ray = new Ray(new Vertex(0, 0, 5), new Vertex(1, 1, 5));
		Intersection intersection = rectangle.intersection(ray, RayIntersectionMode.betweenOriginAndPassThroughPoint);
		Assert.assertFalse(intersection.doesIntersect());
	}

	@Test
	public void centerReturnsCenterPointOfRectangle() throws Exception {
		Rectangle rectangle = new Rectangle(
				new Vertex(10, 25, 17),
				new Vertex(100, 25, 17),
				new Vertex(10, 100, 17)
		);
		Point center = rectangle.center();
		assertPointEquals(new Vertex(55, 62.5, 17), center);
	}
}
